/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.core;

import java.util.List;

import jFamilyTree.model.INode;

/**
 * Transformer
 * 
 * @author Bernard Bou
 */
public class Transformer
{
	// D A T A

	/**
	 * Current transform
	 */
	private HyperTransform theTransform;

	/**
	 * Whether orientation is preserved
	 */
	private boolean thePreserveOrientationFlag;

	// C O N S T R U C T O R

	/**
	 * Constructor
	 */
	public Transformer()
	{
		thePreserveOrientationFlag = true;
		theTransform = new HyperTransform();
	}

	// A C C E S S

	/**
	 * Get transform
	 * 
	 * @return transform
	 */
	public HyperTransform getTransform()
	{
		return theTransform;
	}

	/**
	 * Get whether transform preserves orientation
	 * 
	 * @return true if transform preserves orientation
	 */
	public boolean getPreserveOrientation()
	{
		return thePreserveOrientationFlag;
	}

	/**
	 * Get whether transform preserves orientation
	 * 
	 * @param thisFlag
	 *        if transform is to preserve orientation
	 */
	public void setPreserveOrientation(boolean thisFlag)
	{
		thePreserveOrientationFlag = thisFlag;
	}

	// S E T

	/**
	 * Set current transform
	 * 
	 * @param thisTransform
	 *        new transform
	 */
	public void setTransform(HyperTransform thisTransform)
	{
		theTransform = thisTransform;
	}

	/**
	 * Set current transform as composition of current transform and this transform
	 * 
	 * @param thisTransform
	 *        transform
	 */
	public void composeTransform(HyperTransform thisTransform)
	{
		theTransform = theTransform.compose(thisTransform);
	}

	// O P E R A T I O N

	/**
	 * Transform
	 * 
	 * @param thisNode
	 *        node to apply transform to
	 */
	public synchronized void transform(INode thisNode)
	{
		applyTransform(thisNode, new HyperOptimizedTransform(theTransform));
		thisNode.getLocation().hyper.isBorder = false;
	}

	/**
	 * Reset
	 * 
	 * @param thisNode
	 *        node to apply reset to
	 */
	public synchronized void reset(INode thisNode)
	{
		applyReset(thisNode);
		setTransform(HyperTransform.NULLTRANSFORM);
	}

	// T R A N S F O R M F A C T O R Y

	/**
	 * Make transform
	 * 
	 * @param from
	 *        translation from-point
	 * @param to
	 *        translation to-point
	 * @param thisOrientation
	 *        orientation
	 * @return transform
	 */
	public HyperTransform makeTransform(Complex from, Complex to, Complex thisOrientation)
	{
		if (!thePreserveOrientationFlag)
			return new HyperTransform(new HyperTranslation(from, to, true));

		// orientation preserving
		if (thisOrientation == Complex.ZERO)
			return new HyperRadialOrientationPreservingTransform(from, to, getTransform().map(new Complex(Complex.ZERO)));
		else
			return new HyperOrientationPreservingTransform(from, to, thisOrientation);
	}

	// A P P L Y

	/**
	 * Apply transform
	 * 
	 * @param thisNode
	 *        node to apply transform to
	 * @param thisTransform
	 *        transform to apply
	 */
	private void applyTransform(INode thisNode, IHyperTransform thisTransform)
	{
		if (thisNode == null)
			return;

		// this node
		thisNode.getLocation().hyper.transform(thisTransform);

		// recurse on children
		List<INode> theseChildren = thisNode.getChildren();
		if (theseChildren != null)
			for (INode thisChild : theseChildren)
				applyTransform(thisChild, thisTransform);
	}

	/**
	 * Apply reset
	 * 
	 * @param thisNode
	 *        node to apply reset to
	 */
	private void applyReset(INode thisNode)
	{
		if (thisNode == null)
			return;

		thisNode.getLocation().hyper.reset();

		for (INode thisChild : thisNode.getChildren())
			applyReset(thisChild);
	}
}
